/*
	Copyright 2009 Jason Nottingham

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.


    How to submit a patch:
    1. create or modify a test case (under 'tests') so that it fails to compile/link/run without 
       your change
    2. verify you have the latest sources (resync/merge)
    3. fix or add the missing functionality
    4. verify that your changes fixes the newly broken test case in #1 - it now builds and runs 
       C++ the same as the original C#
    5. create a Subversion-friendly diff or patch file of your changes (including the test cases)
    6. submit your patch at http://gonative.codeplex.com


*/

#pragma once

#pragma message("BasicDataTypes.h")
#pragma warning( disable: 4995 4996 )

#include <windows.h>
#include <strsafe.h>
#include <assert.h>

enum DATATYPE
{
	DATATYPE_OBJECT,
	DATATYPE_INT32,
	DATATYPE_DOUBLE,
	DATATYPE_STRING,
	DATATYPE_LIST,
	DATATYPE_DICTIONARY
};

#include "Collections.h"


#define null NULL

WCHAR *__goNative_DupAscii( const char *c );
CHAR  *__goNative_DupWCHAR( const WCHAR *wc );


class string;

class object
{
public:
	virtual const WCHAR *ToString() const { return L"object"; };
	virtual DATATYPE GetDataType() const { return DATATYPE_OBJECT; };
};

class Int32 : public object
{
protected:
	int value;
public:
	Int32( int i )
	{
		value = i;
	}
	Int32()
	{
		value = 0;
	}
	int Value()
	{
		return value;
	}
	
	DATATYPE GetDataType() const { return DATATYPE_INT32; };

	const WCHAR *ToString() const
	{
		static WCHAR wc[12];
		StringCchPrintf( wc, 12, L"%d", value );
		return wc;
	}


    inline bool operator == (Int32 &i) const
    {
		return ( value == i.Value() );
    }

    inline bool operator == (const int &i) const
    {
		return ( value == i );
    }

    inline bool operator != (Int32 &i) const
    {
		return ( value != i.Value() );
    }

    inline bool operator != (const int &i) const
    {
		return ( value != i );
    }

	
	static int Int32::compare( Int32 *int1, Int32 *int2 )
	{
		int i1 = int1->Value();
		int i2 = int2->Value();
		if ( i1 == i2 )
			return 0;
		return ( i1 > i2 ) ? -1 : 1;
	}

	inline Int32 operator+( Int32 other )
	{
		value += other.Value();
	}
	inline Int32 operator-( Int32 other )
	{
		value -= other.Value();
	}

	inline BOOL operator>( Int32 other )
	{
		return ( value > other.Value() );
	}
	inline BOOL operator>=( Int32 other )
	{
		return ( value >= other.Value() );
	}

	inline BOOL operator<( Int32 other )
	{
		return ( value < other.Value() );
	}
	inline BOOL operator<=( Int32 other )
	{
		return ( value <= other.Value() );
	}

	// prefix ( ++Int32 )
	inline Int32 operator++()
	{
		return ++value;
	}
	// postfix ( Int32++ )
	inline Int32 operator++(int i)
	{
		return value++;
	}

	// prefix ( --Int32 )
	inline Int32 operator--()
	{
		return --value;
	}
	// postfix ( Int32-- )
	inline Int32 operator--(int i)
	{
		return value--;
	}


};


class Double : public object
{
private:
	double value;
public:
	Double( double d )
	{
		value = d;
	}
	Double()
	{
		value = 0;
	}
	double Value()
	{
		return value;
	}

	DATATYPE GetDataType() const { return DATATYPE_DOUBLE; };

	const WCHAR *ToString() const
	{
		static WCHAR wc[12];
		StringCchPrintf( wc, 12, L"%g", value );
		return wc;
	}
	void copy_from( Double other )
	{
		value = other.value;
	}

    inline bool operator == (Double &d) const
    {
		return ( value == d.Value() );
    }

    inline bool operator == (const double &d) const
    {
		return ( value == d );
    }

    inline bool operator != (Double &d) const
    {
		return ( value != d.Value() );
    }

    inline bool operator != (const double &d) const
    {
		return ( value != d );
    }
	
	static int Double::compare( Double *double1, Double *double2 )
	{
		double d1 = double1->Value();
		double d2 = double2->Value();
		if ( d1 == d2 )
			return 0;
		return ( d1 > d2 ) ? -1 : 1;
	}

	inline Double operator+( Double other )
	{
		value += other.Value();
	}
	inline Double operator-( Double other )
	{
		value -= other.Value();
	}

	inline BOOL operator>( Double other )
	{
		return ( value > other.Value() );
	}
	inline BOOL operator>=( Double other )
	{
		return ( value >= other.Value() );
	}

	inline BOOL operator<( Double other )
	{
		return ( value < other.Value() );
	}
	inline BOOL operator<=( Double other )
	{
		return ( value <= other.Value() );
	}

	// prefix ( ++Double )
	inline Double operator++()
	{
		return ++value;
	}
	// postfix ( Double++ )
	inline Double operator++(int i)
	{
		return value++;
	}

	// prefix ( --Double )
	inline Double operator--()
	{
		return --value;
	}
	// postfix ( Double-- )
	inline Double operator--(int i)
	{
		return value--;
	}


	inline Double operator*=(int i)
	{
		value *= i;
		return value;
	}
	inline Double operator*=(double d)
	{
		value *= d;
		return value;
	}
	inline Double operator*=(Int32 i)
	{
		value *= i.Value();
		return value;
	}
	inline Double operator*=(Double d)
	{
		value *= d.Value();
		return value;
	}

	inline Double operator/=(int i)
	{
		value /= i;
		return value;
	}
	inline Double operator/=(double d)
	{
		value /= d;
		return value;
	}
	inline Double operator/=(Int32 i)
	{
		value /= i.Value();
		return value;
	}
	inline Double operator/=(Double d)
	{
		value /= d.Value();
		return value;
	}


};



class string : public object
{
private:
	WCHAR	*wcValue;	// default to WCHAR
	CHAR	*szValue;	// only used if .ToAscii() is called

	inline void FreeValues()
	{
		if ( wcValue )
		{
			free( wcValue );
			wcValue = NULL;
		}
		if ( szValue )
		{
			free( szValue );
			szValue = NULL;
		}
	}

	void initialize()
	{
		wcValue = NULL;
		szValue = NULL;
	}

	void Append_int( int i )
	{
		WCHAR wcTemp[12] = {0};
		StringCchPrintf( wcTemp, 12, L"%d", i );
		AppendString( string(wcTemp) );
	}

	void Append_double( double d )
	{
		WCHAR wcTemp[12] = {0};
		StringCchPrintf( wcTemp, 12, L"%g", d );
		AppendString( string(wcTemp) );
	}


public:
	string();
	~string();
	string( const WCHAR *wc );
	string( const CHAR *c );
	string( const string &copy_from );
	string( int i );
	string( double );
	void copy_from( WCHAR *wc );
	void copy_from( string s );
	string Replace( string find, string replace );
	const WCHAR *ToString();
	const CHAR *ToAscii();
	List<string> *Split( string delimiter );
	List<string> *Split( char delimiter );
	int LastIndexOf( string text );
	int IndexOf( string text );

	DATATYPE GetDataType() const { return DATATYPE_STRING; };

    inline bool operator == (const string &s) const
    {
		return ( wcscmp( wcValue, s.wcValue ) == 0 );
    }

    inline bool operator != (const string &s) const
    {
		return ( wcscmp( wcValue, s.wcValue ) != 0 );
    }


    inline string operator = ( string s )
    {
		this->copy_from(s);
		return string( s );
    }

    inline string operator = (const WCHAR *wc )
    {
		return string( wc );
    }


    inline void /*string*/ operator = (const CHAR *c ) 
    {
		wcValue = __goNative_DupAscii( c );
    }

	inline void AppendString( string toAppend )
	{
		int newSize = Length() + toAppend.Length() + 1;
		WCHAR *wcNewValue = (WCHAR*)malloc( newSize*sizeof(WCHAR) );

		if ( wcNewValue )
		{
			wcscpy( wcNewValue, wcValue ? wcValue : L"" );
			wcscat( wcNewValue, toAppend.ToString() );
			if ( wcValue )
				free( wcValue );
			wcValue = wcNewValue;
		}
	}

	inline string operator+( int i )
	{
		Append_int( i );
		string temp(wcValue);
		return temp;
	}

	inline string operator+( double d )
	{
		Append_double( d );
		string temp(wcValue);
		return temp;
	}

	inline string operator+( string toAppend )
	{
		int newSize = Length() + toAppend.Length() + 1;
		WCHAR *wcNewValue = (WCHAR*)malloc( newSize*sizeof(WCHAR) );

		if ( wcNewValue )
		{
			wcscpy( wcNewValue, wcValue );
			wcscat( wcNewValue, toAppend.ToString() );
			if ( wcValue )
				free( wcValue );
			wcValue = wcNewValue;
		}

		string temp(wcNewValue);
		return temp;
	}

	inline string operator+( object &o )
	{
		string toAppend = o.ToString();
		int newSize = Length() + toAppend.Length() + 1;
		WCHAR *wcNewValue = (WCHAR*)malloc( newSize*sizeof(WCHAR) );

		if ( wcNewValue )
		{
			wcscpy( wcNewValue, wcValue );
			wcscat( wcNewValue, toAppend.ToString() );
			if ( wcValue )
				free( wcValue );
			wcValue = wcNewValue;
		}

		string temp(wcNewValue);
		return temp;
	}


	inline void operator+=( string toAppend )
	{
		int newSize = Length() + toAppend.Length() + 1;
		WCHAR *wcNewValue = (WCHAR*)malloc( newSize*sizeof(WCHAR) );

		if ( wcNewValue )
		{
			wcscpy( wcNewValue, wcValue );
			wcscat( wcNewValue, toAppend.ToString() );
			if ( wcValue )
				free( wcValue );
			wcValue = wcNewValue;
		}
	}


	static BOOL IsNullOrEmpty( WCHAR *wc )
	{
		if ( wc == NULL )
			return TRUE;
		if ( wcslen( wc ) == 0 )
			return TRUE;
		return FALSE;
	}

	static BOOL IsNullOrEmpty( string name )
	{
        const WCHAR *wc = name.ToString();
		if ( wc == NULL )
			return TRUE;
		if ( wcslen( wc ) == 0 )
			return TRUE;
		return FALSE;
	}

	static string string::Format( WCHAR *wcMask )
	{
		string s(wcMask);
		return s;
	}

	static string string::Format( string mask, Int32 o1, Int32 o2 )
	{
		// BUGBUG - this causes an infinite loop
		//return Format( mask.ToString(), o1, o2 );
		return string("");
	}

	//static string string::Format( WCHAR *wcMask, object o1, object o2 )
	static string string::Format( WCHAR *wcMask, Int32 o1, Int32 o2 )
	{
		WCHAR wcTemp[512];

		// BUGBUG - better way?
		wcscpy( wcTemp, wcMask );
		WCHAR *wc = wcTemp;
		while( wc = wcsstr( wc, L"{0}" ) )
		{
			// this is BAADDDDD, only works w/ single digit numbers
			wcscpy( wc, o1.ToString() );
			wc++;
			wcscpy( wc, wc+2 );
		}
		wc = wcTemp;
		while( wc = wcsstr( wc, L"{1}" ) )
		{
			// this is BAADDDDD, only works w/ single digit numbers
			wcscpy( wc++, o2.ToString() );
			wcscpy( wc, wc+2 );
		}
		//StringCchPrintf( wcTemp, 512, wcMask, i1, i2 );
		string s(wcTemp);
		return s;
	}

	static BOOL string::IsNotNullOrEmpty( string s )
	{
		WCHAR *wc = (WCHAR*)s.ToString();
		return ( wc && ( wcslen(wc) > 0 ) );
	}

	static WCHAR *string::Empty()
	{
		return NULL;
	}

	static int string::compare( string *s1, string *s2 )
	{
		return wcscmp( s1->wcValue, s2->wcValue );
	}

	int Length()
	{
		return (wcValue) ? wcslen( wcValue ) : 0;
	}

};


/* for when using std::map
struct string_comparer
{
	bool operator()( string s1, string s2 ) const
	{
		return wcscmp( s1.ToString(), s2.ToString() );
	}
};
*/


class StringBuilder
{
private:
	List<string>	values;
	WCHAR			*wcValue;
	BOOL			fValueUpToDate;

	void toString()
	{
		fValueUpToDate = FALSE;
		if ( wcValue )
			free( wcValue );
		if ( values.Count() == 0 )
			wcValue = L"";
		else
		{
			int i;
			int newSize = 1;	// for null terminator
			for( i = 0; i < values.Count(); i++ )
				newSize += values[i].Length();

			wcValue = (WCHAR*)malloc( newSize * sizeof(WCHAR) );
			if ( !wcValue )
				return;
			wcscpy( wcValue, L"" );
			for( i = 0; i < values.Count(); i++ )
				wcscat( wcValue, values[i].ToString() );
			fValueUpToDate = TRUE;
		}
	}

	void initialize()
	{
		values.Clear();
		wcValue = NULL;
		fValueUpToDate = FALSE;
	}

public:
	StringBuilder()
	{
		initialize();
	}

	~StringBuilder()
	{
		if ( wcValue )
			free( wcValue );
	}

	StringBuilder( int size )
	{
		initialize();
	}

	StringBuilder( string value )
	{
		initialize();
		Append( value );
	}

	void Append( string toAppend )
	{
		fValueUpToDate = FALSE;
		values.Add( toAppend );
	}

	const WCHAR *ToString()
	{
		if ( !fValueUpToDate )
			toString();
		return wcValue;
	}
};







class Console
{
public:
	static void WriteLine()
	{
		wprintf( L"\n" );
	}

	/*
	static void WriteLine( const WCHAR *wc )
	{
		wprintf( L"%s\n", wc );
	}
	static void WriteLine( CHAR *c )
	{
		wprintf( L"%S\n", c );
	}
	*/
	static void WriteLine( string s )
	{
		wprintf( L"%s\n", s.ToString() );
	}

	static void Write( WCHAR *wc )
	{
		wprintf( wc );
	}
	static void Write( CHAR *c )
	{
		wprintf( L"%S", c );
	}
	static void Write( string s )
	{
		wprintf( s.ToString() );
	}

	static void ReadLine()
	{
		WCHAR wc[11]; // BUGBUG - better way?
		_getws_s( wc, 10 );
	}
};

class Path
{
public:
	static string Combine( WCHAR *wcDirectory, WCHAR *wcFileName )
	{
		WCHAR wcCombined[MAX_PATH];
		StringCchPrintf( wcCombined, MAX_PATH, L"%s\\%s", wcDirectory, wcFileName );
		WCHAR *wc = wcCombined;
		while( wc = wcsstr( wc, L"\\\\" ) )
			wcscpy( wc, wc+1 );
		string combined(wcCombined);
		return combined;
	}
	static string Combine( string directory, WCHAR *wcFileName )
	{
		return Combine( (WCHAR*)directory.ToString(), wcFileName );
	}
};


class DateTime
{
private:
	SYSTEMTIME	st;
	DWORD		dwTick;

public:
	DateTime()
	{
		setSystemTimeToNow();
	}

	DateTime( DateTime *copy )
	{
		memcpy( &st, copy->getSystemTime(), sizeof(SYSTEMTIME) );
	}

	static DateTime Now()
	{
		static DateTime now;
		now.setSystemTimeToNow();
		return now;
	}

	const SYSTEMTIME* getSystemTime()
	{
		return &st;
	}

	void setSystemTimeToNow()
	{
		GetSystemTime( &st );
		dwTick = GetTickCount();
	}

	DWORD getTickCount()
	{
		return dwTick;
	}

	const WORD Day()
	{
		return st.wDay;
	}
	const WORD Month()
	{
		return st.wMonth;
	}
	const WORD Year()
	{
		return st.wYear;
	}
	const WORD Hour()
	{
		return st.wHour;
	}
	const WORD Minute()
	{
		return st.wMinute;
	}
	const WORD Second()
	{
		return st.wSecond;
	}
	const WORD Milliseconds()
	{
		return st.wMilliseconds;
	}

	
};

class TimeSpan : object 
{
private:
	DateTime startDateTime;
	DateTime endDateTime;

public:

	TimeSpan( DateTime end, DateTime start )
	{
		startDateTime = start;
		endDateTime = end;
	}

	TimeSpan( DateTime d )
	{
		startDateTime = d;
		endDateTime = d;
	}



	int TotalMilliseconds()
	{
		return ( endDateTime.getTickCount() - startDateTime.getTickCount() );
	}

};

class Thread
{
public:
	static void Sleep( LONG l )
	{
		::Sleep( l );
	}
};



class Debug
{
public:
	static void WriteLine( string text )
	{
		::OutputDebugString( text.ToString() );
	}
};


class Convert
{
public:
	Int32 ToInt32( string text )
	{
		int i = _wtoi( text.ToString() );
		return Int32(i);
	}
};


class Random 
{
};
